package com.rms.hkpc.util;


import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.util.Log;

import com.rms.hkpc.MyApplication;
import com.rms.hkpc.callback.Callback;
import com.rms.hkpc.callback.CallbackWithProgress;
import com.rms.hkpc.constant.AllConstants;
import com.rms.hkpc.db.dao.HttpCacheUtil;
import com.rms.hkpc.db.domain.HttpCache;

import org.jetbrains.annotations.NotNull;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.lang.reflect.Type;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.Call;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Protocol;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;

public class OKHttpUtil {
    private static volatile OKHttpUtil instance;
    private static final long MAX_SIZE = 8 * 1024 * 1024;
    //    public final static String NET_URL = "http://192.168.1.128:1188";
    public final static String NET_URL = "http://synergosiot.ddns.net/Synenvtal";
    //    public final static String NET_URL = "http://192.168.1.188:8001/synCloud1";
    public final static String IMG_URL = NET_URL + "/api/utils/images/400/";
    public final static String VIDEO_URL = NET_URL + "/api/utils/voices/";
    public static final String VERSION_NET_URL = "http://synergosgps.ddns.net:8001/synSystem";
    private static final String VERSION_TOKEN = "56efbbf00223e9f72e935752c2537b88";
    private OkHttpClient okHttpClient;
    private static Handler handler;
    private ExecutorService executorService;
    public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

    public OKHttpUtil() {
        executorService = Executors.newCachedThreadPool();
        handler = new Handler(Looper.getMainLooper());
        File dir = MyApplication.getContext().getFilesDir();
        Cache cache = new Cache(dir, MAX_SIZE);
        okHttpClient = new OkHttpClient.Builder()
                .cache(cache)
                .connectTimeout(8, TimeUnit.SECONDS)
                .addInterceptor(new CacheInterceptor())
                .addInterceptor(new TokenInterceptor())
                .addNetworkInterceptor(new HttpCacheInterceptor())
                .build();
    }

    public static OKHttpUtil getInstance() {
        if (instance == null) {
            synchronized (OKHttpUtil.class) {
                if (instance == null) {
                    instance = new OKHttpUtil();
                }
            }
        }
        return instance;
    }

    public void getVersion(final String url, final Callback callback, final Type typeofT) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                Request request = new Request.Builder()
                        .get()
                        .addHeader("token", VERSION_TOKEN)
                        .url(url)
                        .build();
                Call call = okHttpClient.newCall(request);
                call.enqueue(new okhttp3.Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull IOException e) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onFailed(new IOException());
                            }
                        });
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        String result = response.body().string();
                        Log.i("onResponse", url + "result:" + result);
                        if (TextUtils.isEmpty(result)) {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onFailed(new IOException());
                                }
                            });
                        } else {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                        callback.onSuccess(o);
                                    } catch (Exception e) {
                                        callback.onFailed(e);
                                    }
                                }
                            });
                        }
                    }
                });
            }
        });
    }

    public void getAsync(final String url, final Callback callback, final Type typeofT) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                Log.d(AllConstants.TAG, url);
                Request request = new Request.Builder()
                        .url(url)
                        .get()
                        .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                        .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                        .build();
                okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onFailed(e);
                            }
                        });
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        final String result = response.body().string();
                        Log.d(AllConstants.TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onFailed(new IOException());
                                }
                            });
                        } else {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                        callback.onSuccess(o);
                                    } catch (Exception e) {
                                        callback.onFailed(e);
                                        e.printStackTrace();
                                    }
                                }
                            });
                        }
                    }
                });
            }
        });
    }

    public void postAsync(String url, Object json, Callback callback, Type typeofT) {
        postAsync(url, GsonUtil.getInstance().toJson(json), callback, typeofT);
    }

    public void postAsync(final String url, final String json, final Callback callback, final Type typeofT) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                RequestBody body = RequestBody.create(json, JSON);
                Log.d(AllConstants.TAG, json);
                Request request = new Request.Builder()
                        .url(url)
                        .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                        .addHeader("Accept-language", LanguageUtils.getInstance().getLanguage())
                        .post(body)
                        .build();
                okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onFailed(e);
                            }
                        });
                    }

                    @Override
                    public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException {
                        final String result = response.body().string();
                        Log.d(AllConstants.TAG, result);
                        if (TextUtils.isEmpty(result)) {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onFailed(new IOException());
                                }
                            });
                        } else {
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    try {
                                        Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                                        callback.onSuccess(o);
                                    } catch (Exception e) {
                                        callback.onFailed(e);
                                    }
                                }
                            });
                        }
                    }
                });
            }
        });
    }

    /**
     * 上傳文件
     *
     * @param url
     * @param filePath
     * @param callback
     * @param typeofT
     */
    public void uploadFileAsync(String url, String filePath, final Callback callback, final Type typeofT) {
        File file = new File(filePath);
        RequestBody fileBody = RequestBody.create(MediaType.parse("application/octet-stream"), file);
        RequestBody requestBody = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart("file", file.getName(), fileBody)
                .build();

        final Request request = new Request.Builder()
                .url(url)
                .addHeader("Authorization", "Bearer " + SPUtil.getInstance().getString(AllConstants.TOKEN))
                .post(requestBody)
                .build();
        okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        callback.onFailed(e);
                    }
                });
            }

            @Override
            public void onResponse(@NotNull final Call call, @NotNull Response response) throws IOException {
                String result = response.body().string();
                Log.d(AllConstants.TAG, result);
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Object o = GsonUtil.getInstance().fromJson(result, typeofT);
                            callback.onSuccess(o);
                        } catch (Exception e) {
                            callback.onFailed(e);
                        }
                    }
                });
            }
        });
    }

    public void downloadFile(final String url, final File file, final CallbackWithProgress callback) {
        executorService.execute(new Runnable() {
            @Override
            public void run() {
                if (!file.exists()) {
                    file.getParentFile().mkdirs();
                }
                Request request = new Request.Builder()
                        .url(url)
                        .addHeader("token", VERSION_TOKEN)
                        .get()
                        .build();
                okHttpClient.newCall(request).enqueue(new okhttp3.Callback() {
                    @Override
                    public void onFailure(@NotNull Call call, @NotNull final IOException e) {
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                callback.onFailed(e);
                            }
                        });
                    }

                    @Override
                    public void onResponse(@NotNull final Call call, @NotNull Response response) throws IOException {
                        InputStream is = null;
                        OutputStream os = null;
                        try {
                            final long totalLen = response.body().contentLength();
                            is = response.body().byteStream();
                            os = new FileOutputStream(file);

                            byte[] buffer = new byte[8 * 1024];
                            int curLen = 0;
                            int bufferLen;
                            while (!call.isCanceled() && (bufferLen = is.read(buffer)) != -1) {
                                os.write(buffer, 0, bufferLen);
                                os.flush();
                                curLen += bufferLen;

                                final int finalCurLen = curLen;
                                handler.post(new Runnable() {
                                    @Override
                                    public void run() {
                                        callback.onProgress((int) (finalCurLen * 1.0f / totalLen * 100));
                                    }
                                });
                            }
                            if (call.isCanceled()) {
                                return;
                            }
                            try {
                                file.setExecutable(true, false);
                                file.setReadable(true, false);
                                file.setWritable(true, false);
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onSuccess(file);
                                }
                            });
                        } catch (final Exception e) {
                            if (call.isCanceled()) {
                                return;
                            }
                            e.printStackTrace();
                            handler.post(new Runnable() {
                                @Override
                                public void run() {
                                    callback.onFailed(e);
                                }
                            });
                        } finally {
                            if (is != null) {
                                is.close();
                            }
                            if (os != null) {
                                os.close();
                            }
                        }
                    }
                });
            }
        });
    }
}

class CacheInterceptor implements Interceptor {

    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Request request = chain.request();
        Response response;
        if (request.url().toString().equals(OKHttpUtil.NET_URL + "/api/esp/workOrders/update")
                || request.url().toString().equals(OKHttpUtil.NET_URL + "/api/esp/workOrders/add")
                || request.url().toString().equals(OKHttpUtil.NET_URL + "/api/users/switchLang")
                || request.url().toString().equals(OKHttpUtil.NET_URL + "/api/users/switchProj")
                || request.url().toString().equals(OKHttpUtil.NET_URL + "/api/users/login")) {
            response = getResponse(chain, CacheControl.FORCE_NETWORK);
        } else {
            if (request.method().equals("POST")) {
                Buffer buffer = new Buffer();
                request.body().writeTo(buffer);
                String params = buffer.readString(Charset.forName("UTF-8"));
                String url = request.url().toString();
                if (NetworkUtil.getInstance().isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        MediaType type = originalResponse.body().contentType();
                        byte[] bs = originalResponse.body().bytes();
                        response = originalResponse.newBuilder()
                                .removeHeader("Pragma")
                                .removeHeader("Cache-Control")
                                .header("Cache-Control", "public, max-age=" + 60 * 60 * 24)
                                .body(ResponseBody.create(type, bs))
                                .build();
                        HttpCacheUtil httpCacheUtil = new HttpCacheUtil();
                        httpCacheUtil.insertOrReplaceCache(new HttpCache(null, url, params, new String(bs, "GB2312")));
                    } catch (IOException e) {
                        response = getCacheResponse(url, params, request);
                        e.printStackTrace();
                    }

                } else {
                    response = getCacheResponse(url, params, request);
                }
            } else {
                if (NetworkUtil.getInstance().isConnected()) {
                    try {
                        Response originalResponse = chain.proceed(request);
                        originalResponse.close();
                        response = getResponse(chain, CacheControl.FORCE_NETWORK);
                    } catch (IOException e) {
                        response = getResponse(chain, CacheControl.FORCE_CACHE);
                        e.printStackTrace();
                    }
                } else {
                    response = getResponse(chain, CacheControl.FORCE_CACHE);
                }
            }
        }
        return response;
    }

    private Response getCacheResponse(String url, String params, Request request) {
        HttpCacheUtil httpCacheUtil = new HttpCacheUtil();
        String b = httpCacheUtil.getHttpCache(url, params);
        return new Response.Builder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, only-if-cached, max-stale=" + 60 * 60 * 24 * 28)
                .body(ResponseBody.create(OKHttpUtil.JSON, b.getBytes()))
                .request(request)
                .protocol(Protocol.HTTP_1_1)
                .code(200)
                .message("")
                .build();
    }

    private Response getResponse(Chain chain, CacheControl forceCache) throws IOException {
        return chain.proceed(chain.request()
                .newBuilder()
                .cacheControl(forceCache)
                .build()
        );
    }
}

class HttpCacheInterceptor implements Interceptor {

    @NotNull
    @Override
    public Response intercept(@NotNull Chain chain) throws IOException {
        Response response = chain.proceed(chain.request());
        return response.newBuilder()
                .removeHeader("Pragma")
                .removeHeader("Cache-Control")
                .header("Cache-Control", "public, max-age=" + 24 * 30 * 6000)
                .build();
    }
}
